using Engine.Graphics;
using Engine.Graphics.Shaders;
using Engine.Graphics.VertexStructures;
using Engine.Shape;
using SharpDX;
using System;
using System.Collections.Generic;

namespace ModelViewer
{
    internal class SelectedObject : IRenderableObject
    {
        #region Private Fields

        private Engine.Engine engine;
        private MaterialManager materialManager;
        private EnumRenderMode renderMode = EnumRenderMode.Mesh;
        private SceneryObject scenerieObject;
        private List<int> selectedSubsets = new List<int>();
        private ShaderManager shaderManager;

        #endregion Private Fields

        #region Public Constructors

        public SelectedObject(Engine.Engine engine, ShaderManager shaderManager, MaterialManager materialManager)
        {
            this.engine = engine;
            this.shaderManager = shaderManager;
            this.scenerieObject = new SceneryObject(engine);
            this.materialManager = materialManager;
        }

        #endregion Public Constructors

        #region Internal Constructors

        internal SelectedObject(Engine.Engine device, MaterialManager materialManager, ShaderManager shaderManager, SceneryObject scenerieObject)
            : this(device, shaderManager, materialManager)
        {
            this.scenerieObject = scenerieObject;
        }

        #endregion Internal Constructors

        #region Public Events

        public event EventHandler NewShape;

        #endregion Public Events

        #region Public Properties

        public SceneryObject ScenerieObject
        {
            get { return scenerieObject; }
            set
            {
                scenerieObject = value;
                this.selectedSubsets.Clear();
            }
        }

        public List<int> SelectedSubsets
        {
            get { return selectedSubsets; }
            set { selectedSubsets = value; }
        }

        #endregion Public Properties

        #region Internal Properties

        internal EnumRenderMode RenderMode
        {
            get { return renderMode; }
            set { renderMode = value; }
        }

        #endregion Internal Properties

        #region Public Methods

        public void Dispose()
        {
            if (this.ScenerieObject != null)
                this.ScenerieObject.Dispose();
            this.ScenerieObject = null;
        }

        public void Render(int renderStep)
        {
            if (this.ScenerieObject != null)
            {
                this.engine.Grafic.BlendStateEnableAlpha(true);
                this.engine.Grafic.DepthStateEnableZ(true);
                switch (renderMode)
                {
                    case EnumRenderMode.Mesh:
                        if (this.scenerieObject.Mesh != null)
                        {
                            lock (this.engine.Grafic.Device.ImmediateContext)
                                this.engine.Grafic.Device.ImmediateContext.InputAssembler.PrimitiveTopology = SharpDX.Direct3D.PrimitiveTopology.TriangleList;
                            this.engine.Grafic.SetFillmodeSolid();
                            this.ScenerieObject.Mesh.Render(0);
                        }
                        break;

                    case EnumRenderMode.MeshWithWireframe:
                        // Draw Solid Mesh
                        if (this.ScenerieObject.Mesh == null)
                            break;
                        lock (this.engine.Grafic.Device.ImmediateContext)
                            this.engine.Grafic.Device.ImmediateContext.InputAssembler.PrimitiveTopology = SharpDX.Direct3D.PrimitiveTopology.TriangleList;

                        this.engine.Grafic.SetFillmodeSolid();
                        this.engine.Grafic.BlendStateEnableAlpha(true);
                        this.engine.Grafic.DepthStateEnableZ(true);
                        for (int i = 0; i < this.selectedSubsets.Count; i++)
                        {
                            ShaderBase effect = this.ScenerieObject.Mesh.ShaderManager.SetEffectValues(
                                (this.ScenerieObject.Mesh.materialsIDs[this.selectedSubsets[i]]),
                                this.ScenerieObject.Mesh.MaterialManager);
                            this.scenerieObject.Mesh.ShaderManager.SetShader(effect);
                            this.ScenerieObject.Mesh.DrawSubset(this.selectedSubsets[i]);
                        }

                        // Draw Wireframe Mesh
                        this.engine.Grafic.SetWireframe();
                        //if (this.ScenerieObject.Mesh.ShaderManager != null)
                        //    this.ScenerieObject.Mesh.ShaderManager.SetModulate(0.0f);
                        this.ScenerieObject.Mesh.Render(0);

                        //this.engine.Grafic.SetFillmodeSolid();
                        //this.ScenerieObject.Mesh.Render();
                        //if (this.ScenerieObject.Mesh.ShaderManager != null)
                        //    this.ScenerieObject.Mesh.ShaderManager.SetModulate(1.0f);

                        break;

                    case EnumRenderMode.Shape:
                        lock (this.engine.Grafic.Device.ImmediateContext)
                            this.engine.Grafic.Device.ImmediateContext.InputAssembler.PrimitiveTopology = SharpDX.Direct3D.PrimitiveTopology.LineList;
                        this.engine.Grafic.ShaderManager[0].SetShader(this.engine.Grafic.ShaderManager[0].ColorShader);

                        this.engine.Grafic.DepthStateEnableZ(false);
                        this.engine.Grafic.BlendStateEnableAlpha(false);
                        if (this.ScenerieObject.Shape != null)
                            this.ScenerieObject.Shape.DrawSubset();
                        break;
                }
            }
        }

        #endregion Public Methods

        #region Internal Methods

        internal void CenterX()
        {
            this.scenerieObject.CenterX();
        }

        internal void CenterZ()
        {
            this.scenerieObject.CenterZ();
        }

        internal void MirrorX()
        {
        }

        internal void Rescale(float scale)
        {
            if (scenerieObject.Mesh == null)
                return;

            MyOwnVertex.MyPositionNormalTextured[] vertices = this.scenerieObject.Mesh.Vertices;
            for (int i = 0; i < vertices.Length; i++)
            {
                MyOwnVertex.MyPositionNormalTextured vertex = vertices[i];
                vertex.Position = Vector3.Multiply(vertex.Position, scale);
                vertices[i] = vertex;
            }

            this.scenerieObject.Mesh.UpdateVertexbuffer();
            this.scenerieObject.Shape.Transform(Vector3.Zero, 0, scale);
            Border[] borders = this.scenerieObject.Shape.GetBorders();
            this.scenerieObject.Shape.Scale = 1.0f;
            this.scenerieObject.Shape.SetBorders(borders);
            this.scenerieObject.Shape.UpdateVertexBuffer(new Color4()
            {
                Alpha = 1.0f,
                Blue = 0.0f,
                Green = 1.0f,
                Red = 1.0f
            });
        }

        internal void SetMesh(Engine.FileFormats3D.WavefrontObjects wavefront)
        {
            this.materialManager.ResetMaterials();
            Meshobject mesh = Meshobject.FromWavefrontObject(this.engine.Device, wavefront, this.materialManager, this.shaderManager);

            if (this.ScenerieObject.Mesh != null)
            {
                this.ScenerieObject.Mesh.Dispose();
                this.ScenerieObject.Mesh = null;
            }

            this.ScenerieObject.Mesh = mesh;

            // Update the Vertexbuffer.
            this.ScenerieObject.Update(Vector3.Zero, 0.0f, 1.0f);
        }

        internal void SetShape(Engine.FileFormats3D.WavefrontObjects wavefront)
        {
            Meshobject mesh = Meshobject.FromWavefrontObject(this.engine.Device, wavefront, this.materialManager, this.shaderManager);
            Shapes shape = Shapes.ConvertFromMesh(mesh);

            SetShape(shape);
        }

        internal void SetShape(Shapes shape)
        {
            if (this.ScenerieObject.Shape != null)
            {
                this.ScenerieObject.Shape.Dispose();
                this.ScenerieObject.Shape = null;
            }
            this.ScenerieObject.Shape = shape;
            this.ScenerieObject.Shape.UpdateVertexBuffer(new Color4()
            {
                Alpha = 1.0f,
                Blue = 0.0f,
                Green = 0.0f,
                Red = 1.0f
            });

            if (this.NewShape != null)
                this.NewShape(shape, EventArgs.Empty);

            // Update the Vertexbuffer.
            this.ScenerieObject.Update(Vector3.Zero, 0.0f, 1.0f);
        }

        #endregion Internal Methods
    }
}